﻿using DG.Tweening;
using DG.Tweening.Core;
using DG.Tweening.Plugins.Options;
using Gameplay.PVE;
using LogSystem;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.U2D;
using XLua;
using Yoozoo.Framework.Core;
using Yoozoo.Managers.ResourceManagerV2.Runtime;
using Yoozoo.UI.YGUI;
using Random = UnityEngine.Random;
using Vector3 = UnityEngine.Vector3;

namespace Yoozoo.Gta.Common
{
    public class ResourceType
    {
        public const int None = 0;
        public const int Food = 1;
        public const int Timber = 2;
        public const int Stone = 3;
        public const int Gold = 4;
        public const int Gem = 5;
        public const int Action = 6;
        public const int Dollar = 7;
        public const int HeroExp = 8;
        public const int PlayerExp = 9;
        public const int RtsArenaCoin = 16;
        public const int FishingCoin = 18;
        public const int Alloy = 56;
        public const int MoodPoint = 62;
        public const int HeroicProve = 105;
        public const int LovingHeart = 10000;
        public const int BrokenHeart = 10001;
        public const int Like = 10002;

        public static string GetResourceName(int type)
        {
            switch (type)
            {
                case Food:
                    return "食物";

                case Timber:
                    return "木材";

                case Stone:
                    return "石材";

                case Gold:
                    return "金币";

                case Gem:
                    return "宝石";

                case Action:
                    return "行动力";

                case Dollar:
                    return "美钞";

                case HeroExp:
                    return "英雄经验";

                case PlayerExp:
                    return "玩家经验";
                case RtsArenaCoin:
                    return "Rts竞技场代币";
                case Like:
                    return "开门营业点赞";
                case Alloy:
                    return "合金";
                default:
                    return string.Empty;
            }
        }
    }

    public class FlyAnimationMgr : SingletonMono<FlyAnimationMgr>
    {
        public static Vector3 CommonOutside = new Vector3(5000, 5000, 5000);
        public static Vector3 RotateValue = new Vector3(0, 0, 240);

        private ResFlyObjCreator flyObjCreator;
        private ResUIFly resUIFly;
        private SoldierFly2 soldierFly;
        private GameObject flyResRoot;
        private GameObject flyItemRoot;

        public void Init(string sceneName, Action callback = null)
        {
            if (this.flyObjCreator != null)
                callback?.Invoke();
            
            InitRes(sceneName, callback);
        }

        public void InitUIResFly(float minAngle, float maxAngle, float minDis, float maxDis, float timeStage1,
            float speedStage2)
        {
            this.resUIFly = new ResUIFly();
            this.resUIFly.Init(minAngle, maxAngle, minDis, maxDis, timeStage1, speedStage2);
        }

        public void InitSoldierFly(float minAngle, float maxAngle, float minDis, float maxDis, float speedStage1,
            float speedStage2, float flyObjKillDelay)
        {
            this.soldierFly = new SoldierFly2();
            this.soldierFly.Init(minAngle, maxAngle, minDis, maxDis,
                speedStage1, speedStage2, flyObjKillDelay);
        }

        public void InitFlyResRoot()
        {
            flyResRoot = new GameObject("flyResRoot");
            var root = GameObject.Find("RootCanvas");
            if (root)
            {
                flyResRoot.transform.SetParent(root.transform);
                flyResRoot.transform.position = Vector3.zero;
                flyResRoot.transform.localScale = Vector3.one;
            }
        }
        
        public void InitFlyItemRoot()
        {
            flyItemRoot = new GameObject("flyItemRoot");
            var root = GameObject.Find("RootCanvas");
            if (root)
            {
                flyItemRoot.transform.SetParent(root.transform);
                flyItemRoot.transform.position = Vector3.zero;
                flyItemRoot.transform.localScale = Vector3.one;
            }
        }

        public GameObject GetFlyResRoot()
        {
            return flyResRoot;
        }
        
        public GameObject GetFlyItemRoot()
        {
            return flyItemRoot;
        }

        public void PlayResUIFly(int resType, int count, float startX, float startY, float startZ, float endX,
            float endY, float endZ)
        {
            var flyObjs = GetResFlyObjs(resType, count);
            this.resUIFly.Play(new Vector3(startX, startY, startZ), new Vector3(endX, endY, endZ),
                flyObjs, null, (objs) =>
                {
                    foreach (var obj in objs)
                        GameObject.Destroy(obj);
                });
        }

        // 先用作参数，等有时间在整合代码
        public SoldierFly2 GetSoldierFlyArg()
        {
            return this.soldierFly;
        }

        public List<GameObject> GetResFlyObjs(int type, int count)
        {
            if (this.flyObjCreator == null)
                return null;
            
            return this.flyObjCreator.CreateObjs(type, count);
        }

        #region PVE挂机

        public class RPGHangingDropData
        {
            public const int angle_1_min = 35;// 区间角度1 最小值
            public const int angle_1_max = 65;// 区间角度1 最大值
            public const int angle_2_min = 125;// 区间角度2 最小值
            public const int angle_2_max = 155;// 区间角度2 最大值

            public const int FloatDisMin = 10;// 最短距离
            public const int FloatDisMax = 50;// 最大距离
            public const float FloatSpeed = 360;// 飞行速度 舍弃
            public const float FlyTime = 0.5f;// 飞行时间
            public const float SpawnInterval = 0;// 间隔时间
        }

        public class RPGHangingDropParticle
        {
            public const float FlySpeed = 10;
            public const float FlyStartPercent = 0.5f;
        }

        private int uniqueID = 0;
        private Dictionary<int, RPGHangingFlyTask> flyTaskDict = new Dictionary<int, RPGHangingFlyTask>();
        private LuaFunction hangingEndAni;

        public int RPGHangingDropFly(List<GameObject> flyObjs, Vector3 startPos, Vector3 endPos,
            Action<GameObject> onOneFinish = null, Action<List<GameObject>> onAllFinish = null,
            bool needRotate = false, float Scale = 1, bool flyToEnd = false)
        {
            if (flyObjs == null || flyObjs.Count == 0)
                return -1;

            var id = ++this.uniqueID;
            var co = StartCoroutine(RPGHangingDropFlyCo(id, flyObjs, startPos, endPos, onOneFinish, onAllFinish,
                needRotate, Scale, flyToEnd));
            /*var save = new RPGHangingFlyTask();
            save.objs = flyObjs;
            save.coroutine = co;
            save.callbackOne = onOneFinish;
            save.callbackAll = onAllFinish;
            this.flyTaskDict.Add(id, save);*/
            return id;
        }

        public int RPGHangingDropFly(GameObject particleGameObject, Vector3 endPos, int count,
            Action<GameObject> callback = null)
        {
            var ps = particleGameObject.transform.Find("Piao").GetComponent<ParticleSystem>();
            if (ps == null)
                return -1;

            StartCoroutine(RPGHangingDropFlyCo(particleGameObject, ps, endPos, count, callback));
            return 0;
        }

        private IEnumerator RPGHangingDropFlyCo(GameObject particleGameObject, ParticleSystem particleSystem,
            Vector3 endPos, int count, Action<GameObject> callback = null)
        {
            var timer = 0f;
            var frame = 0;
            var particleNum = 32;
            var particles = new ParticleSystem.Particle[particleNum];
            List<float> startFlyTime = new List<float>();
            Vector3[] startFlyPosition = new Vector3[particleNum];
            Dictionary<int, float> flyingIndexDict = new Dictionary<int, float>(particleNum);
            var pveCam = PveCameraManager.Instance.mainCamera;
            var arriveCount = 0;
            while (true)
            {
                yield return null;
                timer += Time.deltaTime;
                frame++;
                if (!PveManager.Instance.IsStageActive)
                {
                    break;
                }

                var pCount = particleSystem.GetParticles(particles);
                if (frame == 1)
                {
                    for (var i = 0; i < pCount; i++)
                        startFlyTime.Add(particles[i].startLifetime * RPGHangingDropParticle.FlyStartPercent);
                }

                for (var i = 0; i < pCount; i++)
                {
                    if (flyingIndexDict.ContainsKey(i))
                    {
                        if (flyingIndexDict[i] != -1)
                        {
                            particles[i].remainingLifetime = 1;

                            // 执行位置计算
                            flyingIndexDict[i] += Time.deltaTime;
                            var screen = pveCam.WorldToScreenPoint(particles[i].position);
                            endPos.z = screen.z;
                            var result = pveCam.ScreenToWorldPoint(endPos);
                            var t = flyingIndexDict[i] / startFlyTime[i];
                            if (t > 1)
                                t = 1;

                            particles[i].position =
                                Vector3.Lerp(startFlyPosition[i], result, t * t * t); // DOTween Ease.OutCubic

                            if (t == 1)
                            {
                                // 到达，标记为不刷新位置
                                particles[i].position = CommonOutside;
                                flyingIndexDict[i] = -1;
                                hangingEndAni?.Action(true);
                                arriveCount++;
                            }
                        }
                        else
                            particles[i].position = CommonOutside;
                    }
                    else if (timer >= startFlyTime[i])
                    {
                        var screen = pveCam.WorldToScreenPoint(particles[i].position);
                        endPos.z = screen.z;
                        var result = pveCam.ScreenToWorldPoint(endPos);
                        var dis = Vector3.Distance(particles[i].position, result);
                        var flyTime = dis / RPGHangingDropParticle.FlySpeed;
                        startFlyPosition[i] = particles[i].position;
                        startFlyTime[i] = flyTime; // 原来的值已经没用了复用一个数组
                        flyingIndexDict.Add(i, 0);
                    }
                }

                particleSystem.SetParticles(particles, pCount);
                if (arriveCount == pCount)
                    break;
            }
        }

        public void AddHangingLuaAniFunction(LuaFunction func)
        {
            this.hangingEndAni = func;
        }

        public void FlyResInPve(int resID, Vector3 startPos, Vector3 endPos, int count, LuaFunction callback = null,
            Transform shakeObj = null, float scale = 1)
        {
            var objs = GetResFlyObjs(resID, count);
            if (objs == null)
            {
                callback?.Action(string.Empty);
                return;
            }

            RPGHangingDropFly(objs, startPos, endPos, (go) =>
            {
                if (shakeObj != null && shakeObj.localScale == Vector3.one)
                    shakeObj?.DOScale(Vector3.one * 1.2f, 0.05f).SetEase(Ease.Linear).OnComplete(() =>
                    {
                        shakeObj?.DOScale(Vector3.one, 0.05f).SetEase(Ease.Linear);
                    });

                //go.SetActive(false);
            }, (list) =>
            {
                this.flyObjCreator.ReleaseObjs(resID,objs);
                callback?.Action(string.Empty);
            });
        }

        //任务飞效果
        public void FlyHangingDropItem(Vector3 startPos, Vector3 endPos, int count, string spritePath,
            string qualityImgPath, LuaFunction callback, Transform shakeObj, float scale = 1, bool needRotate = false,
            int startOrder = 500, float itemScale = 33)
        {
            var loader = ResLoader.Alloc();
            loader.Add2Load("Assets/ResourcesAssets/" + spritePath + ".png", typeof(Sprite),
                (success, assetName, asset) =>
                {
                    if (!success)
                    {
                        callback?.Action(spritePath);
                        return;
                    }

                    GameObject uiRoot = GetInstance().GetFlyItemRoot();
                    if (!uiRoot)
                    {
                        callback?.Action(spritePath);
                    }

                    var sprite = asset as Sprite;
                    var objs = new List<GameObject>(count);
                    var loader2 = ResLoader.Alloc();
                    loader2.Add2Load("Assets/ResourcesAssets/Pve/Prefabs/Others/FlyItem.prefab", (b, s, o) =>
                    {
                        if (o is GameObject prefab)
                        {
                            for (var i = 0; i < count; i++)
                            {
                                var go = Instantiate(prefab);
                                go.transform.SetParent(uiRoot.transform);
                                go.transform.localPosition = CommonOutside;

                                go.SetActive(true);
                                Transform tf = go.transform.GetChild(0);
                                tf.GetComponent<SpriteRenderer>().sprite = sprite;
                                tf.localScale = new Vector3(itemScale, itemScale, 1);
                                FlyItemQuality qua = tf.gameObject.GetOrAddComponent<FlyItemQuality>();
                                qua.UpdateQuality(qualityImgPath);
                                SortingGroup sort = go.GetComponent<SortingGroup>();
                                if (sort) sort.sortingOrder = startOrder + i;
                                objs.Add(go);
                            }
                        }
                        
                        //需要异步返回后再处理
                        RPGHangingDropFly(objs, startPos, endPos, (go) =>
                        {
                            if (shakeObj != null && shakeObj.localScale == Vector3.one)
                                shakeObj?.DOScale(Vector3.one * 1.1f, 0.06f).SetEase(Ease.Linear).OnComplete(() =>
                                {
                                    shakeObj?.DOScale(Vector3.one, 0.06f).SetEase(Ease.Linear);
                                });

                            //go.SetActive(false);
                        }, (list) =>
                        {
                            //if (isInPve)
                            //    foreach (var obj in list)
                            //        PveResourceManager.RecoverResource(obj, PveResourceManager.EPveResType.UI, 1);
                            //else
                            foreach (var obj in list)
                                GameObject.Destroy(obj);

                            loader.Recycle2Cache();
                            loader = null;
                            callback?.Action(spritePath);
                        }, needRotate, scale);
                    });
                    loader2.Load();
                });

            loader.Load();
        }

        public void FlyDeblock(Vector3 startPos, Vector3 endPos, int count, string spritePath,
            LuaFunction callback, Transform shakeObj, float scale = 1, bool needRotate = false, int startOrder = 500)
        {
            var loader = ResLoader.Alloc();
            loader.Add2Load("Assets/ResourcesAssets/" + spritePath + ".png", typeof(Sprite),
                (success, assetName, asset) =>
                {
                    if (!success)
                    {
                        callback?.Action(spritePath);
                        return;
                    }

                    GameObject uiRoot = GameObject.Find("RootCanvas");
                    if (!uiRoot)
                    {
                        callback?.Action(spritePath);
                    }

                    var sprite = asset as Sprite;
                    var objs = new List<GameObject>(count);
                    var loader2 = ResLoader.Alloc();
                    loader2.Add2Load("Assets/ResourcesAssets/Pve/Prefabs/Others/FlyDebLock.prefab",
                        (b, s, o) =>
                        {
                            if (b && o is GameObject prefab)
                            {
                                for (var i = 0; i < count; i++)
                                {
                                    var go = Instantiate(prefab);
                                    go.transform.SetParent(uiRoot.transform);
                                    go.transform.localPosition = CommonOutside;
                                    go.transform.localScale = Vector3.one * scale;
                                    go.SetActive(true);
                                    YImage image = go.GetComponent<YImage>();
                                    image.sprite = sprite;
                                    image.SetNativeSize();
                                    objs.Add(go);
                                }
                            }
                            
                            //这里需要在异步加载之后触发
                            RPGHangingDropFly(objs, startPos, endPos, (go) => { }, (list) =>
                            {
                                foreach (var obj in list)
                                    GameObject.Destroy(obj);

                                loader.Recycle2Cache();
                                loader = null;
                                callback?.Action(spritePath);
                            }, needRotate, scale, true);
                        });
                    loader2.Load();
                });

            loader.Load();
        }

        /// <summary>
        /// 飞行动画
        /// </summary>
        /// <param name="id"></param>
        /// <param name="objs"></param>
        /// <param name="startPos"></param>
        /// <param name="endPos"></param>
        /// <param name="onOneFinish"></param>
        /// <param name="onAllFinish"></param>
        /// <param name="needRotate"></param>
        /// <param name="scale"></param>
        /// <param name="flyToEnd">直接飞向目标</param>
        /// <returns></returns>
        private IEnumerator RPGHangingDropFlyCo(int id, List<GameObject> objs, Vector3 startPos, Vector3 endPos,
            Action<GameObject> onOneFinish, Action<List<GameObject>> onAllFinish, bool needRotate, float scale,bool flyToEnd = false)
        {
            var index = 0;
            var count = 0;
            var flag = Random.Range(0, 2) == 1;
            while (index < objs.Count)
            {
                var go = objs[index++];
                go.transform.position = startPos;
                go.transform.localScale = Vector3.one;

                var angle = 0;
                if (flag)
                {
                    angle = Random.Range(RPGHangingDropData.angle_1_min, RPGHangingDropData.angle_1_max);
                }
                else
                {
                    angle = Random.Range(RPGHangingDropData.angle_2_min, RPGHangingDropData.angle_2_max);
                }
                var rad = angle * Mathf.Deg2Rad;
                var dis = Random.Range(RPGHangingDropData.FloatDisMin, RPGHangingDropData.FloatDisMax);
                var tempStartPos = go.transform.position;
                var middlePoint = new Vector3(tempStartPos.x + Mathf.Round(Mathf.Cos(rad) * dis),
                    tempStartPos.y + Mathf.Round(Mathf.Sin(rad) * dis), tempStartPos.z);

                Sequence s = DOTween.Sequence();
                s.SetUpdate(true);

                TweenerCore<Quaternion, Vector3, QuaternionOptions> rotate = null;
                if (needRotate)
                {
                    rotate = go.transform.DORotate(RotateValue, RPGHangingDropData.FlyTime).SetEase(Ease.InOutQuad);
                }

                if (flyToEnd)
                {
                    // 直接飞向目标
                    s.Append(go.transform.DOMove(endPos, RPGHangingDropData.FlyTime).SetEase(Ease.InQuad));
                }
                else
                {
                    float time1 = dis / RPGHangingDropData.FloatSpeed;
                    s.Append(go.transform.DOMove(middlePoint, RPGHangingDropData.FlyTime)).SetEase(Ease.InOutSine);
                    s.Append(go.transform.DOMove(endPos, RPGHangingDropData.FlyTime).SetEase(Ease.InQuad));
                }

                s.AppendCallback(() =>
                {
                    rotate?.Kill();
                    this.hangingEndAni?.Action(true);
                    onOneFinish?.Invoke(go);
                    count++;
                    if (count == objs.Count)
                    {
                        onAllFinish?.Invoke(objs);
                        //不知道有啥用，先删了
                        //this.flyTaskDict.Remove(id);
                    }
                });

                flag = !flag;
                yield return new WaitForSeconds(RPGHangingDropData.SpawnInterval);
            }
        }

        #endregion

        private void InitRes(string sceneName, Action callback = null)
        {
            this.flyObjCreator = new ResFlyObjCreator();
            var t = new List<int>();
            // Lua ESCENE_TYPE
            if (sceneName == "CityScene")
            {
                t.Add(ResourceType.Food);
                t.Add(ResourceType.Timber);
                t.Add(ResourceType.Stone);
                t.Add(ResourceType.Gold);
                t.Add(ResourceType.Gem);
                t.Add(ResourceType.Dollar);
                t.Add(ResourceType.RtsArenaCoin);
                t.Add(ResourceType.FishingCoin);
                t.Add(ResourceType.HeroicProve);
                t.Add(ResourceType.LovingHeart);
                t.Add(ResourceType.BrokenHeart);
                t.Add(ResourceType.MoodPoint);
                t.Add(ResourceType.Like);
                t.Add(ResourceType.Alloy);
            }
            
            this.flyObjCreator.Init(t, callback);
        }

        public void Dispose()
        {
            CommonSpriteManager.GetInstance("CommonSpriteManager").StopAllCoroutines();
            
            if (this.flyObjCreator != null)
            {
                this.flyObjCreator.Dispose();
                this.flyObjCreator = null;
            }
            if (flyResRoot != null)
            {
                Destroy(flyResRoot);
                flyResRoot = null;
            }
        }
    }

    class ResFlyObjCreator
    {
        // 资源飞行动画的序列帧路径
        public const string ResSpriteSheetPath = "Assets/ResourcesAssets/UI/GUI/ResFlySpriteSheet_{0}.spriteatlas";

        // 资源飞行单位预制
        public const string ResFlyPrefabPath = "Assets/ResourcesAssets/Prefabs/UI/Common/CityResFly.prefab";

        private ResLoader resLoader;

        private Dictionary<int, Sprite[]> resSpriteSheet;
        private GameObject resFlyPrefab;
        //已经使用中的
        private List<GameObject> givedObjs = new List<GameObject>(PREFAB_POOL_MAX);
        
        //对象池
        private Dictionary<int,List<List<GameObject>>> gameObjectListPool;
        private const int GAMEOBJECT_LIST_LENGTH = 10;
        private const int GAMEOBJECT_POOL_MAX = 10;
        private List<GameObject> prefabPool;

        private Dictionary<GameObject, SpriteFramePlayer> sfpDic;
        
        private const int PREFAB_POOL_MAX = 20;

        public ResFlyObjCreator()
        {
            this.resLoader = ResLoader.Alloc();
        }

        public void Init(List<int> resTypes, Action callback = null)
        {
            this.resSpriteSheet = new Dictionary<int, Sprite[]>(resTypes.Count);
            sfpDic = new Dictionary<GameObject, SpriteFramePlayer>();
            InitPool(resTypes);
            LoadFramePlayer();
            LoadResSpriteSheet(resTypes, callback);
        }

        private void InitPool(List<int> resTypes)
        {
            gameObjectListPool = new Dictionary<int, List<List<GameObject>>>();//(GAMEOBJECT_POOL_MAX);
            prefabPool = new List<GameObject>(PREFAB_POOL_MAX);
            for (int i = 0; i < resTypes.Count; i++)
            {
                var resType = resTypes[i];
                if (!gameObjectListPool.ContainsKey(resType))
                {
                    var list = new List<List<GameObject>>(GAMEOBJECT_POOL_MAX);
                    for (int j = 0; j < GAMEOBJECT_POOL_MAX; j++)
                    {
                        list.Add(new List<GameObject>(GAMEOBJECT_LIST_LENGTH));
                    }
                    gameObjectListPool.Add(resType,list);
                }
            }
        }

        public void Dispose()
        {
            //ReleaseObjs(this.givedObjs);
            if (givedObjs != null)
            {
                for (int i = 0; i < givedObjs.Count; i++)
                {
                    GameObject.DestroyImmediate(givedObjs[i]);
                }
                givedObjs.Clear();
            }

            if (prefabPool != null)
            {
                for (int i = 0; i < prefabPool.Count; i++)
                {
                    var gameObject = prefabPool[i];
                    if (gameObject != null)
                    {
                        GameObject.Destroy(gameObject);
                    }
                }
                prefabPool.Clear();
            }

            sfpDic?.Clear();
            this.resLoader.Recycle2Cache();
            this.resLoader = null;
        }

        private List<GameObject> GetGameObjectEmptyList(int resType)
        {
            if (gameObjectListPool.TryGetValue(resType, out var list))
            {
                var count = list.Count;
                if (count > 0)
                {
                    var ret = list[count - 1];
                    list.RemoveAt(count - 1);
                    return ret;
                }
            }
            return new List<GameObject>(GAMEOBJECT_LIST_LENGTH);
        }

        private void RecoverGameObjectList(int resType,List<GameObject> list)
        {
            if (gameObjectListPool.TryGetValue(resType, out var pool))
            {
                list.Clear();
                if (pool.Count < GAMEOBJECT_POOL_MAX)
                {
                    pool.Add(list);
                }
            }
        }
        public List<GameObject> CreateObjs(int type, int count)
        {
            if (!this.resSpriteSheet.ContainsKey(type) || this.resFlyPrefab == null)
                return null;

            var ret = GetGameObjectEmptyList(type);//new List<GameObject>(count);
            for (var i = 0; i < count; i++)
                ret.Add(CreateObj(type));

            return ret;
        }

        private SpriteFramePlayer GetSFP(GameObject gameObject)
        {
            if (sfpDic.TryGetValue(gameObject, out var sfp))
            {
                return sfp;
            }
            return gameObject.GetComponentInChildren<SpriteFramePlayer>();
        }
        
        private GameObject InstantiatePrefab(int type = -1)
        {
            var go = GameObject.Instantiate(this.resFlyPrefab, FlyAnimationMgr.GetInstance().GetFlyResRoot().transform);
            go.transform.position = FlyAnimationMgr.CommonOutside;
            var framePlayer = go.GetComponentInChildren<SpriteFramePlayer>();
            sfpDic[go] = framePlayer;
            if (type < 0)
            {
                framePlayer.OnRecover();
            }
            else
            {
                if (resSpriteSheet.TryGetValue(type, out var sprites))
                {
                    framePlayer.Init(sprites);
                }
            }
            return go;
        }
        
        public GameObject CreateObj(int type)
        {
            GameObject go = null;
            SpriteFramePlayer framePlayer = null;
            if (prefabPool.Count > 0)
            {
                go = prefabPool[prefabPool.Count - 1];
                framePlayer = GetSFP(go);
                if (resSpriteSheet.TryGetValue(type, out var sprites))
                {
                    framePlayer.OnGet();
                    framePlayer.Init(sprites);
                }
                prefabPool.RemoveAt(prefabPool.Count - 1);
            }
            else
            {
                go = InstantiatePrefab(type);
            }
            go.transform.position = FlyAnimationMgr.CommonOutside;
            this.givedObjs.Add(go);
            return go;
        }

        public void ReleaseObjs(int resType,List<GameObject> objs)
        {
            if (objs == null)
                return;

            for (var i = 0; i < objs.Count; i++)
            {
                var gameObject = objs[i];
                if (prefabPool.Count < PREFAB_POOL_MAX)
                {
                    if (sfpDic.TryGetValue(gameObject, out var sfp))
                    {
                        sfp.OnRecover();
                    }
                    prefabPool.Add(gameObject);
                }
                else
                {
                    GameObject.Destroy(gameObject);
                }
                    
            }
            RecoverGameObjectList(resType,objs);
        }

        private void LoadFramePlayer()
        {
            this.resLoader.Add2Load(ResFlyPrefabPath, (success, name, asset) =>
            {
                if (!success)
                    return;

                this.resFlyPrefab = asset as GameObject;
                for (int i = 0; i < PREFAB_POOL_MAX; i++)
                {
                    var prefab = InstantiatePrefab();
                    prefabPool.Add(prefab);
                }
            });
        }

        // 加载序列帧图集
        private void LoadResSpriteSheet(List<int> resTypes, Action callback = null)
        {
            var regex = new Regex(@"\(Clone\)");
            foreach (var resType in resTypes)
            {
                this.resLoader.Add2Load(string.Format(ResSpriteSheetPath, resType), typeof(SpriteAtlas),
                    (success, name, asset) =>
                    {
                        if (!success)
                        {
                            Debug.LogErrorFormat(LogModule.Sandbox, "{0} not found", name);
                            return;
                        }

                        var a = asset as SpriteAtlas;
                        if (a == null)
                        {
                            Debug.LogErrorFormat(LogModule.Sandbox, "{0} is not SpriteAtlas ({1})", name, asset.GetType());
                            return;
                        }
                        var sprites = new Sprite[a.spriteCount];
                        a.GetSprites(sprites);
                        Array.Sort(sprites, (sprite, sprite1) =>
                        {
                            var t1 = int.Parse(regex.Replace(sprite.name, ""));
                            var t2 = int.Parse(regex.Replace(sprite1.name, ""));
                            return t1 - t2;
                        });
                        this.resSpriteSheet.Add(resType, sprites);
                    });
            }

            this.resLoader.Load(() => { callback?.Invoke(); });
        }
    }

    class ResUIFly
    {
        private float minAngle;
        private float maxAngle;
        private float minDis;
        private float maxDis;
        private float timeStage1;
        private float speedStage2;

        public void Init(float minAngle, float maxAngle, float minDis, float maxDis, float timeStage1,
            float speedStage2)
        {
            this.minAngle = minAngle;
            this.maxAngle = maxAngle;
            this.minDis = minDis;
            this.maxDis = maxDis;
            this.timeStage1 = timeStage1;
            this.speedStage2 = speedStage2;
        }

        public void Play(Vector3 startPos, Vector3 endPos, List<GameObject> flyObjs, Action<GameObject> onOneComplete,
            Action<List<GameObject>> onAllComplete)
        {
            if (flyObjs == null || flyObjs.Count == 0)
                return;

            var mid = flyObjs.Count / 2;
            var arriveCount = 0;
            for (var i = 0; i < flyObjs.Count; i++)
            {
                var randomAngle = Random.Range(this.minAngle, this.maxAngle);
                var realAngle = 0f;
                if (i < mid)
                    realAngle = 90 + randomAngle;
                else
                    realAngle = 90 - randomAngle;

                var rad = realAngle * Mathf.Deg2Rad;
                var dis = Random.Range(this.minDis, this.maxDis);
                var middlePoint = new Vector3(startPos.x + Mathf.Round(Mathf.Cos(rad) * dis),
                    startPos.y + Mathf.Round(Mathf.Sin(rad) * dis), startPos.z);

                var flyTask = new CommonDoTweenFlyTask();
                flyTask.AddWayPoint(middlePoint, this.timeStage1);

                var dis2 = Vector3.Distance(middlePoint, endPos);
                flyTask.AddWayPoint(endPos, dis2 / this.speedStage2);
                flyObjs[i].transform.position = startPos;
                flyTask.Play(flyObjs[i], (go) =>
                {
                    onOneComplete?.Invoke(go);
                    arriveCount += 1;
                    if (arriveCount == flyObjs.Count)
                        onAllComplete?.Invoke(flyObjs);
                });
            }
        }
    }

    public class SoldierFly2
    {
        public float minAngle;
        public float maxAngle;
        public float minDis;
        public float maxDis;
        public float speedStage1;
        public float speedStage2;
        public float flyObjKillDelay;

        public void Init(float minAngle, float maxAngle, float minDis, float maxDis, float speedStage1,
            float speedStage2, float flyObjKillDelay)
        {
            this.minAngle = minAngle;
            this.maxAngle = maxAngle;
            this.minDis = minDis;
            this.maxDis = maxDis;
            this.speedStage1 = speedStage1;
            this.speedStage2 = speedStage2;
            this.flyObjKillDelay = flyObjKillDelay;
        }
    }

    class CommonDoTweenFlyTask
    {
        private List<MoveTweenData> movePoints = new List<MoveTweenData>();
        private Tween tween;

        public void AddWayPoint(Vector3 target, float time, Ease easeType = Ease.Linear)
        {
            this.movePoints.Add(new MoveTweenData() {endPoint = target, time = time, easeType = easeType});
        }

        public void Play(GameObject flyer, Action<GameObject> callback)
        {
            if (flyer == null)
                return;

            if (this.movePoints.Count <= 0)
            {
                callback?.Invoke(flyer);
                return;
            }

            _Play(flyer, callback, 0);
        }

        private void _Play(GameObject flyer, Action<GameObject> callback, int index)
        {
            if (index >= this.movePoints.Count)
            {
                callback?.Invoke(flyer);
                return;
            }

            var _flyData = this.movePoints[index];
            flyer.transform.DOMove(_flyData.endPoint, _flyData.time).SetEase(_flyData.easeType).OnComplete(() =>
            {
                _Play(flyer, callback, ++index);
            });
        }
    }

    class MoveTweenData
    {
        public Vector3 endPoint;
        public float time;
        public Ease easeType;
    }

    class RPGHangingFlyTask
    {
        public List<GameObject> objs;
        public Coroutine coroutine;
        public Action<GameObject> callbackOne;
        public Action<List<GameObject>> callbackAll;
    }

    public class BezierCurveCalculator
    {
        public static Vector3[] GetBezierPath2Order(Vector3 start, Vector3 middle, Vector3 end, int segment = 10)
        {
            var ret = new Vector3[segment + 1];
            ret[0] = start;
            ret[ret.Length - 1] = end;
            var gap = 1.0f / segment;
            var bezier = new Bezier2Order(start, middle, end);
            for (var i = 1; i < segment; i++)
                ret[i] = bezier.GetPoint(gap * i);

            return ret;
        }
    }

    public class Bezier2Order
    {
        private Vector3 start;
        private Vector3 middle;
        private Vector3 end;

        public Bezier2Order(Vector3 start, Vector3 middle, Vector3 end)
        {
            this.start = start;
            this.middle = middle;
            this.end = end;
        }

        public Vector3 GetPoint(float t)
        {
            var pa = this.start + (this.middle - this.start) * t;
            var pb = this.middle + (this.end - this.middle) * t;
            return pa + (pb - pa) * t;
        }
    }
}
